/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	EventSystem.h
*
*	Description -	Event manager using the listener and dispatcher design pattern
*
*	Comments	-
*
*	Modification History:
*			Name			   Date					Description
*			MrCodeSushi	-	03/07/2009	-	Creation of this file
**************************************************************************************/

#ifndef _PSX_EVENT_SYSTEM_H_
#define _PSX_EVENT_SYSTEM_H_

#include "PulseSTD.h"
#include "IEvent.h"
#include "SmartPointer.h"
#include "Queue.h"
#include "List.h"
#include "Array.h"
#include "Map.h"
#include "HashTable.h"
#include "../External/FastDelegate/FastDelegate.h"

namespace Pulse
{
	typedef SmartPointer<IEvent> EventPtr;
	typedef fastdelegate::FastDelegate1<IEvent*> EventListener;

	class EventSystem
	{
	public:

		enum CONSTANTS { INFINITY = 0xFFFFFFFF };

		explicit EventSystem( void );

		~EventSystem( void );

		// this, class member function
		template < typename ThisObject, typename Function >
		BOOL AddListener( const EEvent::Type event, ThisObject thisObject, Function func );

		//// static functions
		//template < typename Function >
		//BOOL AddListener( const EEvent::Type event, Function &func );

		// this, class member function
		template < typename ThisObject, typename Function >
		BOOL RemoveListener( const EEvent::Type event, ThisObject thisObject, Function func );

		//// static functions
		//template < typename Function >
		//BOOL RemoveListener( const EEvent::Type event, Function &func );

		BOOL AddListener( const EEvent::Type event, const EventListener *pListener );
		
		BOOL RemoveListener( const EEvent::Type event, const EventListener *pListener );

		// NOTE: Execute event immediately
		void TriggerEvent( const IEvent *pEvent );

		// NOTE: Queue event object
		void PostEvent( const EventPtr &event );

		// NOTE: This queues the event in the unmanaged queue but be aware 
		//	that it is unmanaged. The poster is responsible for the memory 
		//	management of the posted event.
		void PostEventUnmanaged( IEvent *pEvent );

		void ProcessEvents( FLOAT maxTime = (FLOAT)INFINITY );

		//BOOL AbortEvent( const EEvent *pType );

	private:

		// We employ a double-buffered event queue
		enum PRIVATE_CONSTANTS { NUM_QUEUES = 2 };

		typedef Queue< EventPtr >						EventQueue;
		typedef Queue< IEvent* >						EventQueueUnmanaged;
		typedef Array< EventListener >					ListenerList;
		typedef Map< EEvent::Type, ListenerList >		ListenerMap;	// Will this be faster if we use hashtable?

		ListenerMap m_listeners;

		EventQueue			m_eventQueues[NUM_QUEUES];
		EventQueueUnmanaged	m_eventQueuesUnmanaged[NUM_QUEUES];
		INT					m_activeQueue;

	};

	PSX_INLINE EventSystem::EventSystem( void )
		: m_activeQueue(0)
	{
	}

	PSX_INLINE EventSystem::~EventSystem( void )
	{

	}

	PSX_INLINE void EventSystem::PostEvent( const EventPtr &event )
	{
		m_eventQueues[m_activeQueue].Enqueue( event );
	}

	PSX_INLINE void EventSystem::PostEventUnmanaged( IEvent *pEvent )
	{
		m_eventQueuesUnmanaged[m_activeQueue].Enqueue( pEvent );
	}

	// this, method
	template < typename ThisObject, typename Function >
	BOOL EventSystem::AddListener(  const EEvent::Type event, ThisObject thisObject, Function func )
	{
		return AddListener( event, &fastdelegate::MakeDelegate( thisObject, func ) );
	}

	//template < typename Function >
	//BOOL EventSystem::AddListener( const EEvent::Type event, Function &func )
	//{
	//	EventListener listener;
	//	listener.bind( &func );
	//	return AddListener( event, &listener );
	//}

	// this, class member function
	template < typename ThisObject, typename Function >
	BOOL EventSystem::RemoveListener( const EEvent::Type event, ThisObject thisObject, Function func )
	{
		return RemoveListener( event, &fastdelegate::MakeDelegate( thisObject, func ) );
	}

	//// static functions
	//template < typename Function >
	//BOOL EventSystem::RemoveListener( const EEvent::Type event, Function &func )
	//{
	//	EventListener listener;
	//	listener.bind( &func );
	//	return RemoveListener( event, &listener );
	//}

}

#endif /* _PSX_EVENT_SYSTEM_H_ */